using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Net;
using System.IO;
using Frankfort.Threading.Internal;
using System.Threading;

class HttpDownloadTask
{
	public delegate void HttpDownloadHandler(string url, string localPath, DownloadError status);
	public delegate void HttpDownloadHandler2(HttpDownloadTask task, HttpDownloadRequest request, string url, string localPath, DownloadError status);
	
	string url;
	string localPath;
    string hash;
	uint size;
	int maxRetryCount;
	HttpRedirectHelper redirectHelper;
	HttpDownloadHandler2 callBack;
	int callBackNum;
	int successCallBackNum;
	bool _isCancel;
	
	public Dictionary<HttpDownloadHandler, string> handlersMap = new Dictionary<HttpDownloadHandler, string>();
	public List<HttpDownloadRequest> requests;
	
	public int getRequestProgress()
	{
		int progress = 0;
		for(int i = 0; i < requests.Count; i++)
		{
			progress += requests[i].progress;
		}
		return progress;
	}
	
	public float getRequestSpeed()
	{
		float sumSpeed = 0;
		for(int i = 0; i < requests.Count; i++)
		{
			sumSpeed += requests[i].getSpeed();
		}
		return sumSpeed;
	}
	
	public int getDownloadTotalConnectCount()
	{
		int count = 0;
		for(int i = 0; i < requests.Count; i++)
		{
			count += requests[i].getTotalConnectCount();
		}
		return count;
	}
	
	public int getBadDownloadRetryCount()
	{
		int count = 0;
		for(int i = 0; i < requests.Count; i++)
		{
			count += requests[i].getBadRetryCount();
		}
		return count;
	}
	
	public bool isRequestRunning()
	{
		for(int i = 0; i < requests.Count; i++)
		{
			if(requests[i].getStartTime() != 0)
			{
				return true;
			}
		}
		return false;
	}
	
	public bool isRequestWaiting()
	{
		return !isRequestRunning();
	}
	
	public bool isRequestConnecting()
	{
		for(int i = 0; i < requests.Count; i++)
		{
			if(requests[i].isConnecting())
			{
				return true;
			}
		}
		return false;
	}
	
	public bool isRequestReadyConnect()
	{
		for(int i = 0; i < requests.Count; i++)
		{
			if(requests[i].isReadyConnect())
			{
				return true;
			}
		}
		return false;
	}
	
	public HttpDownloadTask(string url, string hash, string localPath, uint size, int maxRetryCount, HttpRedirectHelper redirectHelper,  HttpDownloadHandler2 callback)
	{
		this.url = url;
        this.hash = hash;
		this.localPath = localPath;
		this.size = size;
		this.maxRetryCount = maxRetryCount;
		this.redirectHelper = redirectHelper;
		this.callBack = callback;
	}
	
	public List<HttpDownloadRequest> createRequests(uint requestDownloadSize, uint decopmressSize, int maxRequestNum, uint compressType)
	{
		if(requests == null)
		{
			requests = new List<HttpDownloadRequest>();
            if (size > 0)
            {
                int requestNum = (int)(size / requestDownloadSize);
                if (size % requestDownloadSize != 0)
                {
                    requestNum++;
                }
    			
                requestNum = Math.Min(maxRequestNum, requestNum);

                requestDownloadSize = size / (uint)requestNum;
                uint remain = size % (uint)requestNum;

                uint offset = 0;
                for (int i = 0; i < requestNum; i++)
                {
                    uint requestSize = requestDownloadSize;
                    if (i < remain)
                    {
                        requestSize++;
                    }

                    string tempLocalPath = null;
                    if (requestNum > 1)
                    {
                        tempLocalPath = localPath + "_" + offset + "_" + requestSize;
                    }
                    else
                    {
                        tempLocalPath = localPath;
                    }

                    HttpDownloadRequest request = new HttpDownloadRequest(url, tempLocalPath, offset, requestSize, decopmressSize, size, compressType, this.maxRetryCount, this.redirectHelper, DownloadCallback, moveFile);
                    requests.Add(request);

                    offset += requestSize;
                }
            }
            else
            {
                string tempLocalPath = localPath;
                HttpDownloadRequest request = new HttpDownloadRequest(url, tempLocalPath, 0, 0, decopmressSize, size, compressType, this.maxRetryCount, this.redirectHelper, DownloadCallback, moveFile);
                requests.Add(request);
            }
		}
		return requests;
	}
	
	private bool moveFile()
	{
		//		string srcPath = null;
		//		HashSet<string> outputs = new HashSet<string>();
		//		Dictionary<HttpDownloadTask.HttpDownloadHandler, string>.Enumerator enumerator = handlersMap.GetEnumerator();
		//		while(enumerator.MoveNext())
		//		{
		//			if(!outputs.Contains(enumerator.Current.Value))
		//			{
		//				string dir = Path.GetDirectoryName(enumerator.Current.Value);
		//				Directory.CreateDirectory(dir);
		//				
		//				CommonUtil.Delete(enumerator.Current.Value);
		//				if(srcPath == null)
		//				{
		//					if(CommonUtil.Move(localPath, enumerator.Current.Value))
		//					{
		//						srcPath = enumerator.Current.Value;
		//					}
		//					else
		//					{
		////						status = DownloadError.FileOperationFailed;
		//					}
		//				}
		//				else
		//				{
		//					if(!CommonUtil.Copy(srcPath, enumerator.Current.Value))
		//					{
		////						status = DownloadError.FileOperationFailed;
		//					}
		//				}
		//				outputs.Add(enumerator.Current.Value);
		//			}
		//		}
		
		return true;
	}
	
	public void CallBack(DownloadError status)
	{
		//			string srcPath = null;
		
		//			HashSet<string> outputs = new HashSet<string>();
		Dictionary<HttpDownloadTask.HttpDownloadHandler, string>.Enumerator enumerator = handlersMap.GetEnumerator();
		while(enumerator.MoveNext())
		{
			if(status == DownloadError.None)
			{
				//					if(!outputs.Contains(enumerator.Current.Value))
				//					{
				//						string dir = Path.GetDirectoryName(enumerator.Current.Value);
				//						Directory.CreateDirectory(dir);
				//
				//						CommonUtil.Delete(enumerator.Current.Value);
				//						if(srcPath == null)
				//						{
				//							if(CommonUtil.Move(localPath, enumerator.Current.Value))
				//							{
				//								srcPath = enumerator.Current.Value;
				//							}
				//							else
				//							{
				//								status = DownloadError.FileOperationFailed;
				//							}
				//						}
				//						else
				//						{
				//							if(!CommonUtil.Copy(srcPath, enumerator.Current.Value))
				//							{
				//								status = DownloadError.FileOperationFailed;
				//							}
				//						}
				//						outputs.Add(enumerator.Current.Value);
				//					}
			}
			enumerator.Current.Key(url, enumerator.Current.Value, status);
		}
	}
	
    private bool CheckHash()
    {
        if (!string.IsNullOrEmpty(this.hash))
        {
            string fileHash = MD5Hash.GetMD5HashFromFile(localPath);
            if (fileHash != this.hash)
            {
                Debug.LogError("hash of downloaded file is not same: " + localPath + "  " + this.hash + "  " + fileHash);
                if(VersionEx.compile_type != CompileType.release)
                {
                    throw new System.Exception("hash of downloaded file is not same: " + localPath + "  " + this.hash + "  " + fileHash);
                }
                return false;
            }
        }
        return true;
    }

	private void DownloadCallback(HttpDownloadRequest request, string url, String localPath2, DownloadError status)
	{
		callBackNum++;
		if(status == DownloadError.None)
		{
			successCallBackNum++;
		}
		
		if(callBack != null)
		{
			callBack(this, request, url, localPath2, status);
		}
		
		if(callBackNum == requests.Count)
		{
			if(status == DownloadError.None && requests.Count > 1)
			{
				Thread _workThread = SingleThreadStarter.StartSingleThread(() =>
				                                                           {
					FileStream outStream = null;
					FileStream inStream = null;
					try
					{
						outStream = File.Create(localPath);
						if(outStream != null)
						{
							byte[] buf = new byte[1024];
							for(int i = 0; i < requests.Count; i++)
							{
								inStream = File.OpenRead(requests[i]._localPath);
								if(inStream != null)
								{
									while(true)
									{
										int readLen = inStream.Read(buf, 0, buf.Length);
										if(readLen <= 0)
										{
											inStream.Close();
											inStream = null;
											break;
										}
										lock(this)
										{
											if(_isCancel)
											{
												break;
											}
											outStream.Write(buf, 0, readLen);
											outStream.Flush();
										}
									}
									
									if(_isCancel)
									{
										break;
									}
								}
								else
								{
									status = DownloadError.FileOperationFailed;
									break;
								}
							}
							
							if(status == DownloadError.None)
							{
								for(int i = 0; i < requests.Count; i++)
								{
									CommonUtil.Delete(requests[i]._localPath);
								}
							}
						}
						else
						{
							status = DownloadError.FileOperationFailed;
						}
					}
					catch(System.Exception e)
					{
						status = DownloadError.FileOperationFailed;
						Debug.LogException(e);
					}
					finally
					{
						if(outStream != null)
						{
							outStream.Close();
						}
						
						if(inStream != null)
						{
							inStream.Close();
						}
					}
					

                    if (status == DownloadError.None)
                    {
                        if(!CheckHash())
                        {
                            status = DownloadError.CheckHashFailed;
                            if(VersionEx.compile_type == CompileType.release)
                            {
                                CommonUtil.Delete(localPath);
                            }
                        }
                    }

                    finish(status);
					
				});
			}
			else
			{
                if (status == DownloadError.None)
                {
                    if(!CheckHash())
                    {
                        status = DownloadError.CheckHashFailed;
                        CommonUtil.Delete(localPath);
                    }
                }
                
				finish(status);
			}
		}
	}
	
	private void finish(DownloadError error)
	{
		if(!Loom.CheckIfMainThread())
		{
			MainThreadDispatcher.DispatchToMainThread((object o) => {finish(error);}, null);
			return;
		}
		
		if(callBack != null)
		{
			HttpDownloadHandler2 temp = callBack;
			callBack = null;
			temp(this, null, url, localPath, error);
		}
	}
	
	public void Cancel()
	{
		for(int i = 0; i < requests.Count; i++)
		{
			requests[i].Cancel();
		}
		
		lock(this)
		{
			_isCancel = true;
		}
		
		callBack = null;
	}
}
